// Copyright (c) 2015-2022, Lawrence Livermore National Security, LLC.
// See top-level LICENSE file for details.

/// \file CaliperMetadataDB.h
/// \brief CaliperMetadataDB class declaration

#ifndef CALI_CALIPERMETADATADB_H
#define CALI_CALIPERMETADATADB_H

#include "RecordProcessor.h"

#include "../common/Attribute.h"
#include "../common/CaliperMetadataAccessInterface.h"

#include <map>
#include <memory>
#include <string>
#include <unordered_map>

namespace cali
{

class Node;
class Variant;

typedef std::map<cali_id_t, cali_id_t> IdMap;

/// \brief Maintains a context tree and provides metadata information.
/// \ingroup ReaderAPI

class CaliperMetadataDB : public CaliperMetadataAccessInterface
{
    struct CaliperMetadataDBImpl;
    std::unique_ptr<CaliperMetadataDBImpl> mP;

public:

    CaliperMetadataDB();

    ~CaliperMetadataDB();

    //
    // --- I/O API
    //

    Node* merge_node(cali_id_t node_id, cali_id_t attr_id, cali_id_t prnt_id, const Variant& v_data, IdMap& idmap);

    Node* merge_node(cali_id_t node_id, cali_id_t attr_id, cali_id_t prnt_id, const std::string& data, IdMap& idmap);

    EntryList merge_snapshot(
        size_t          n_nodes,
        const cali_id_t node_ids[],
        size_t          n_imm,
        const cali_id_t attr_ids[],
        const Variant   values[],
        const IdMap&    idmap
    ) const;

    /// \brief Merge snapshot record bound to metadata DB \a db
    ///   into this metadata DB
    EntryList merge_snapshot(const CaliperMetadataAccessInterface& db, const std::vector<Entry>& rec);

    Entry merge_entry(cali_id_t node_id, const IdMap& idmap);
    Entry merge_entry(cali_id_t attr_id, const std::string& data, const IdMap& idmap);

    void merge_global(cali_id_t node_id, const IdMap& idmap);
    void merge_global(cali_id_t attr_id, const std::string& data, const IdMap& idmap);

    //
    // --- Query API
    //

    Node* node(cali_id_t id) const;

    Attribute get_attribute(cali_id_t id) const;
    Attribute get_attribute(const std::string& name) const;

    std::vector<Attribute> get_all_attributes() const;

    //
    // --- Manipulation
    //

    Node* make_tree_entry(std::size_t n, const Node* nodelist[], Node* parent = 0);
    Node* make_tree_entry(std::size_t n, const Attribute attr[], const Variant data[], Node* parent = 0);

    Attribute create_attribute(
        const std::string& name,
        cali_attr_type     type,
        int                prop,
        int                meta      = 0,
        const Attribute*   meta_attr = nullptr,
        const Variant*     meta_data = nullptr
    );
    //
    // --- Globals
    //

    /// \brief Get global entries (entries with the CALI_ATTR_GLOBAL flag)
    std::vector<Entry> get_globals();

    /// \brief Set a global entry
    void set_global(const Attribute& attr, const Variant& value);

    /// \brief Import global entries from metadata DB \a db into this
    ///   metadata DB
    std::vector<Entry> import_globals(CaliperMetadataAccessInterface& db);

    /// \brief Import globals in record \a rec from metadata DB \a db into this
    ///   metadata DB
    std::vector<Entry> import_globals(CaliperMetadataAccessInterface& db, const std::vector<Entry>& globals);

    /// \brief Add a set of attribute aliases
    ///
    /// This adds a "attribute.alias" meta-attribute for the aliased attribute
    /// to export alias information in a cali data stream.
    /// Currently this is limited to new attributes created with
    /// create_attribute() in this database. It does not apply to imported
    /// attributes.
    void add_attribute_aliases(const std::map<std::string, std::string>& aliases);

    /// \brief Add a set of attribute units
    ///
    /// This adds a "attribute.unit" meta-attribute for the aliased attribute
    /// to export alias information in a cali data stream.
    /// Currently this is limited to new attributes created with
    /// create_attribute() in this database. It does not apply to imported
    /// attributes.
    void add_attribute_units(const std::map<std::string, std::string>& aliases);

    /// \brief print usage statistics
    std::ostream& print_statistics(std::ostream& os);
};

} // namespace cali

#endif
